package com.stars.config;


import com.stars.common.base.BaseCode;
import com.stars.common.base.BaseConstant;
import com.stars.common.base.BaseMsg;
import com.stars.common.base.ResultMsg;
import com.stars.common.cons.RedisCons;
import com.stars.common.maps.JsonToMapUtils;
import com.stars.common.maps.MapSort;
import com.stars.common.result.Conversion;
import com.stars.entity.dto.sys.SystemPermissionDTO;
import com.stars.services.system.AuthAccessService;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author alex
 * @version 1.0.0
 * @description 此项目全局拦截器。所有访问都要经过此拦截器拦截请求。此拦截器为拦截JWT验证
 * @date 2017年4月20日 上午2:28:30
 * @note 注意事项
 */
@Slf4j
public class SecurityInterceptor implements HandlerInterceptor {

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private AuthAccessService authAccessServiceImpl;
    @Value("${exclude.url}")
    private String[] excludeUrl;

    //在业务处理器处理请求之前被调用。预处理，可以进行编码、安全控制、权限校验等处理；
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Map<String, String> map = MapSort.getParamMap(request.getParameterMap());
        log.info("获取请求的所有参数:" + map);
        String url = request.getRequestURI();
        try {
            boolean bl = excludePathPatternsUrl(excludeUrl, url);
            if (bl) {
                return true;
            }
            //请求拦截，jwt验证不通过则返回token失效，token有效则获取其权限
            String token = request.getHeader(BaseConstant.ACCESS_TOKEN);

            String redisUserId = "";
            //String userId = cookies;
            if (null == token) {
                Cookie[] cookies = request.getCookies();
                if(null ==cookies){
                    Conversion.outPrint(response, ResultMsg.customMsg(BaseCode.TOKEN_NOT_EXISTED, BaseMsg.TOKEN_NOT_EXISTED, null));
                    return false;
                }
                for (Cookie cookie : cookies) {
                    if (StringUtils.equals(cookie.getName(), BaseConstant.USER_ID)) {
                        //根据用户id获取token
                        redisUserId = cookie.getValue();
                        break;
                    }
                }
                if (StringUtils.isEmpty(redisUserId)) {
                    Conversion.outPrint(response, ResultMsg.customMsg(BaseCode.TOKEN_NOT_EXISTED, BaseMsg.TOKEN_NOT_EXISTED, null));
                    return false;
                }
            }else {
                //根据token获取缓存值
                redisUserId = redisTemplate.opsForValue().get(token);
            }
            if (StringUtils.isBlank(redisUserId)) {
                Conversion.outPrint(response, ResultMsg.customMsg(BaseCode.TOKEN_NOT_EXISTED, BaseMsg.TOKEN_NOT_EXISTED, null));
                return false;
            }
            //如果是管理员账号，不过滤
            if (authAccessServiceImpl.userIsRoot(redisUserId)) {
                return true;
            }
            //注解式验证请求方法
//            HandlerMethod handlerMethod = (HandlerMethod) handler;
//            Method method = handlerMethod.getMethod();
//            UserAuthenticate userAuthenticate = method.getAnnotation(UserAuthenticate.class);
//            //如果没有加注解则userAuthenticate为null
//            if (Objects.nonNull(userAuthenticate)) {
//                //userAuthenticate.permission()取出permission判断是否需要校验权限
//                if (userAuthenticate.permission()){
//                    return true;
//                }
//            }
            log.info("登录token还存在，redisToken:" + token + ",redisUserId:" + redisUserId + ",允许登录");
            /** 根据用户id查询该用户拥有的url资源权限 **/
            //根据菜单更新的时间，选择查询方式
            String menuLastUpdate = redisTemplate.opsForValue().get(RedisCons.MENU_LAST_UPDATE);
            String userLoginDate = redisTemplate.opsForValue().get(RedisCons.USER_LOGIN_DATE + redisUserId);
            if (StringUtils.isNotBlank(menuLastUpdate) && StringUtils.isNotBlank(userLoginDate)) {
                Long menuLong = new Long(menuLastUpdate);
                Long userLong = new Long(userLoginDate);
                //如果用户登录的时间小于菜单资源更新的时间，重新设置菜单
                if (userLong < menuLong) {
                    SystemPermissionDTO dto = new SystemPermissionDTO();
                    dto.setUserId(redisUserId);
                    List<SystemPermissionDTO> userMenusList = authAccessServiceImpl.findUserMenusList(dto);
                    Map<String, Object> urlMap = new HashMap<>();
                    userMenusList.forEach(per -> {
                        if (StringUtils.isNotBlank(per.getPerUrl())) {
                            urlMap.put(per.getPerUrl(), per.getPerName());
                        }
                    });
                    redisTemplate.opsForValue().set(RedisCons.MENU_USER_URL + redisUserId, JsonToMapUtils.mapToJson(urlMap));
                }
            }
            String userUrlMap = redisTemplate.opsForValue().get(RedisCons.MENU_USER_URL + redisUserId);
            if (StringUtils.isBlank(userUrlMap)) {
                Conversion.outPrint(response, ResultMsg.customMsg(BaseCode.NOT_PERMISSION, BaseMsg.NOT_PERMISSION, null));
                return false;
            }
            Map<String, Object> redisUrlMap = JsonToMapUtils.jsonToMapJSONObject(userUrlMap);
            if (redisUrlMap.containsKey(url)) {
                return true;
            }

            Conversion.outPrint(response, ResultMsg.customMsg(BaseCode.NOT_PERMISSION, BaseMsg.NOT_PERMISSION, null));
            return false;
        } catch (ExpiredJwtException e) {
            Conversion.outPrint(response, ResultMsg.customMsg(BaseCode.TOKEN_NOT_EXISTED, BaseMsg.TOKEN_NOT_EXISTED, null));
            return false;
        }
    }

    //在业务处理器处理请求执行完成后，生成视图之前执行。
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub

    }

    //在DispatcherServlet完全处理完请求后被调用，可用于清理资源等。返回处理
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }


    /**
     * 过滤url
     *
     * @param excludePaths url 数组
     * @param url          当前地址
     * @return
     */
    public boolean excludePathPatternsUrl(String[] excludePaths, String url) {
        for (String str : excludePaths) {
            if (str.equals(url)) {
                return true;
            }
        }
        return false;
    }

}
